#include "alt_up_vga.h"
#include "system.h"
#include "alt_up_vga_regs.h"

/////////////////////////////////////////////////////////////////////////////
/*
 * Internal Utility Functions:
 * these functions are not supposed to be called directly by the user so they
 * are not defined in the header file
 */
/////////////////////////////////////////////////////////////////////////////

/**
 * @brief calculate the SRAM address for pixel according to the provided x and y coordniates
 *
 * @param x_pos  x coordniate, ranging from 0 to X (max number of pixels per row) under the current resolution
 * @param y_pos  y coordniate, ranging from 0 to Y (max number of pixels per column) under the current resolution
 *
 * @return the converted address for pixel
 * @sa the datasheet for the VGA Core on the DE2 Board
 * @note the function requires that the input are in the valid range
 *
 **/
alt_u32 get_pix_SRAM_addr(unsigned x_pos, unsigned y_pos)
{
	//assume valid inputs
	alt_u32 addr = 0x00000000;
	addr |= ( (x_pos << ALT_UP_VGA_PIX_X_COORD_OFST) & ALT_UP_VGA_PIX_X_COORD_MSK );
	addr |= ( (y_pos << ALT_UP_VGA_PIX_Y_COORD_OFST) & ALT_UP_VGA_PIX_Y_COORD_MSK );
	return addr; 
}

/**
 * @brief calculate the SRAM address for character according to the provided x and y coordniates
 *
 * @param x_pos  x coordniate, ranging from 0 to X (max number of pixels per row) under the current resolution
 * @param y_pos  y coordniate, ranging from 0 to Y (max number of pixels per column) under the current resolution
 *
 * @return the converted address for character
 * @sa the datasheet for the VGA Core on the DE2 Board
 * @note the function requires that the input are in the valid range
 *
 **/
alt_u32 get_char_SRAM_addr(unsigned x_pos, unsigned y_pos)
{
	//assume valid inputs
	alt_u32 addr = 0x00000000;
	addr |= ( (x_pos << ALT_UP_VGA_CHAR_X_COORD_OFST) & ALT_UP_VGA_CHAR_X_COORD_MSK );
	addr |= ( (y_pos << ALT_UP_VGA_CHAR_Y_COORD_OFST) & ALT_UP_VGA_CHAR_Y_COORD_MSK );
	return addr; 
}


/**
 * @brief send a pixel to the Instruction Register of the VGA controller
 *
 * @param cmd  the command bits 
 *
 * @return 0 for success
 **/
int alt_up_vga_send_pixel_to_addr(alt_u32 data, alt_u32 addr)
{
	IOWR_16DIRECT(ALT_UP_VGA_AVALON_PIXEL_SLAVE_BASE, addr, (alt_u16)(data & ALT_UP_VGA_PIX_COLOR_MASK) ) ;
	return 0;
}

/**
 * @brief send a char to the Instruction Register of the VGA controller
 *
 * @param cmd  the command bits 
 *
 * @return 0 for success
 **/
int alt_up_vga_send_char_data_to_addr(alt_u32 data, alt_u32 addr)
{
	IOWR_32DIRECT(ALT_UP_VGA_AVALON_CHARACTER_SLAVE_BASE, addr, data) ;
	return 0;
}

////////////////////////////////////////////////////////////////////////////
/*
 * Interface Functions : 
 * The interface of these fucntions are provided to the user. See the header
 * file for a detailed description of each function
 */
////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
// Pixel Mode Functions
//////////////////////////////////////////////////////////////////////////////////////
int alt_up_vga_draw_pixel(alt_u32 pixel, unsigned x, unsigned y)
{
	// boundary check
	if (x >= ALT_UP_VGA_PIX_X_RESOLUTION || y >= ALT_UP_VGA_PIX_Y_RESOLUTION )
		return -1;
	// get address
	alt_u32 addr = get_pix_SRAM_addr(x, y);
	//send the pixel to SRAM memory address, which is mapped to (x,y) on the VGA screen
	return alt_up_vga_send_pixel_to_addr(pixel, addr);
}

//////////////////////////////////////////////////////////////////////////////////////
// Character Mode Functions
//////////////////////////////////////////////////////////////////////////////////////

int alt_up_vga_draw_char_1b(alt_u8 ch, unsigned x, unsigned y)
{
	// boundary check
	if (x >= ALT_UP_VGA_CHAR_X_RESOLUTION || y >= ALT_UP_VGA_CHAR_Y_RESOLUTION )
		return -1;
	alt_u32 addr = get_char_SRAM_addr(x, y);
	return alt_up_vga_send_char_data_to_addr(ch, addr);
}

/* ************************************************************************
 * The following functions is not supported by the hardware currently. This
 * will be supported in future releases.
 * ************************************************************************/

int alt_up_vga_draw_char_9b(alt_u8 ch, unsigned x, unsigned y, unsigned r, unsigned g, unsigned b)
{
	// boundary check
	if (x >= ALT_UP_VGA_CHAR_X_RESOLUTION || y >= ALT_UP_VGA_CHAR_Y_RESOLUTION )
		return -1;
	if ( r > 7 || g > 7 || b > 7)
		return -1;
	alt_u32 addr = get_char_SRAM_addr(x, y);
	alt_u32 data = 0;
	data |= (ch & 0x7F); // ASCII part
	data |= (r << 14) | (g << 11) | (b << 8); // RGB part. See VGA document for details on data format

	return alt_up_vga_send_char_data_to_addr(data, addr);
}

int alt_up_vga_draw_char_8b(alt_u8 ch, unsigned x, unsigned y, unsigned fg, unsigned bg, unsigned transparency)
{
	// boundary check
	if (x >= ALT_UP_VGA_CHAR_X_RESOLUTION || y >= ALT_UP_VGA_CHAR_Y_RESOLUTION )
		return -1;
	if ( fg > 15 || bg > 15 || transparency > 1)
		return -1;
	alt_u32 addr = get_char_SRAM_addr(x, y);
	alt_u32 data = 0;
	data |= (ch & 0x7F); // ASCII part
	data |= (transparency << 16) | (bg << 12) | (fg << 8); // T, BG, FG part. See VGA document for details on data format

	return alt_up_vga_send_char_data_to_addr(data, addr);
}

int alt_up_vga_draw_char_4b(alt_u8 ch, unsigned x, unsigned y, unsigned fg)
{
	// boundary check
	if (x >= ALT_UP_VGA_CHAR_X_RESOLUTION || y >= ALT_UP_VGA_CHAR_Y_RESOLUTION )
		return -1;
	if ( fg > 15 )
		return -1;
	alt_u32 addr = get_char_SRAM_addr(x, y);
	alt_u32 data = 0;
	data |= (ch & 0x7F); // ASCII part
	data |= (fg << 8); // FG part. See VGA document for details on data format

	return alt_up_vga_send_char_data_to_addr(data, addr);
}
